package engine

type ConcurrentEngine struct {
	Scheduler        Scheduler
	WorkerCount      int
	ItemChan         chan Item
	RequestProcessor Processor
}

type Processor func(Request) (ParseResult, error)

type Scheduler interface {
	ReadyNotifier
	Submit(Request)
	WorkerChan() chan Request // 我有一个worker给我哪一个channel
	//ConfigureMasterWorkerChan(chan Request)
	//WorkerReady(chan Request)
	Run()
}

type ReadyNotifier interface {
	// 提取出该方法，是接口轻量化
	WorkerReady(chan Request)
}

func (e *ConcurrentEngine) Run(seeds ...Request) {

	//in := make(chan Request)
	out := make(chan ParseResult)
	//e.Scheduler.ConfigureMasterWorkerChan(in)  // 将已经定义的channel送入Scheduler
	e.Scheduler.Run()

	for i := 0; i < e.WorkerCount; i++ {
		// 由Scheduler分配worker channel
		e.createWorker(e.Scheduler.WorkerChan(), out, e.Scheduler)
	}
	for _, r := range seeds {
		if isDuplicate(r.Url) {
			continue
		}
		e.Scheduler.Submit(r)
	}
	//itemCount := 0
	for {
		// 将所有的Item打印，将Request传递给scheduler
		result := <-out
		for _, item := range result.Items {
			//log.Printf("Got item: #%d: %v", itemCount, item)
			//itemCount++
			go func() { e.ItemChan <- item }()
		}
		for _, request := range result.Requests {
			if isDuplicate(request.Url) {
				continue
			}
			e.Scheduler.Submit(request)
		}
	}
}

// 整个Scheduler作为参数太重了，把要用到的WorkerReady()分离出来
func (e *ConcurrentEngine) createWorker(in chan Request, out chan ParseResult, ready ReadyNotifier) {
	go func() {
		for {
			// tell scheduler i'm ready
			ready.WorkerReady(in)
			// 2. 从in中收到request任务
			request := <-in // 循环等待
			// 收到任务后woker开始工作
			// call rpc
			result, err := e.RequestProcessor(request)
			if err != nil {
				continue
			}
			out <- result // 能成功送出比如上方能收到，循环等待
		}
	}()
}

var visitedUrls = make(map[string]bool)

// 去重
func isDuplicate(url string) bool {
	if visitedUrls[url] {
		return true
	}

	visitedUrls[url] = true
	return false
}
